package com.chess.annotation.demo;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.util.DisplayMetrics;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.RelativeLayout;

@SuppressLint("ViewConstructor")
public abstract class AnnotationView extends RelativeLayout implements OnTouchListener {

	protected Context context;
	/**
	 * to control whether we show reshape button at the right-bottom corner of annotation view. By default, it is true,
	 * which means it is text annotation.
	 */
	protected boolean enableReshape = true;
	/**
	 * the close button at the top-left corner
	 */
	protected ImageView closeView = null;
	/**
	 * the reshape button at the right-bottom corner.
	 */
	protected View reshapeView = null;

	/**
	 * the bound view contains four light blue lines for every annotation to indicate the boundaries of the annotation
	 * view.
	 */
	protected AnnotationBoundView annotationBoundView = null;

	/**
	 * the radius of the reshape button and the image button
	 */
	protected int operationRadius;

	/**
	 * the minimum height of the annotation view could be. It is set to as three times large as the radius of the
	 * reshape button
	 */
	protected int miniHeight;
	/**
	 * the maximum height of the annotation view could be. It is computed out by using screen height to minus
	 * notification bar height and title bar height.
	 */
	protected int maxHeight;

	/**
	 * the current annotation view that's being dragging or resizing.
	 */
	protected ViewGroup currentAnnotationView = null;

	/**
	 * the default width and height of the annotation view. For the time being, we have not used these values
	 */
	protected int widthDefault = 0;
	protected int heightDefault = 0;

	/**
	 * the top-left point of the annotation view when being dragged or moved.
	 */
	protected float annotationInitialLeft;
	protected float annotationInitialTop;

	/**
	 * the width and height of the annotation view being dragged or moved.
	 */
	protected int annotationInitialWidth;
	protected int annotationInitialHeight;

	/**
	 * the center point of the annotation view when being dragged or moved.
	 */
	protected float xPivot;
	protected float YPivot;

	/**
	 * the start point when the annotation is being dragged or moved.
	 */
	protected float xStart;
	protected float yStart;

	/**
	 * the last point of the finger in the screen. This is used to move or reshape the annotation view.
	 */
	protected int xLast;
	protected int yLast;

	/**
	 * the width and height of the device information.
	 */
	protected int screenWidth;
	protected int screenHeight;

	/**
	 * the mode of the annotation.
	 */
	protected State mode = State.NONE;

	/**
	 * the current scale of the annotation view. This is used to determine how the annotation view should be scaled. It
	 * also controls the text size of AnnotationTextView.
	 */
	protected float scaleCurrent = 1;

	/**
	 * the last touch scale after users finish operating the annotation view. This is used to determine the scaleCurrent
	 */
	protected float scaleLastTouch = 1;

	/**
	 * the distance between the two point when fingers touch the screen to zoom in or out the annotation view.
	 */
	protected float disLastTouch = 0;

	/**
	 * limitation for annotation scale.
	 */
	protected float scaleMax = 10.0f;
	protected float scaleMini = 0.5f;

	protected float touchSlop;

	protected AnnotationDelegate delegate;

	public AnnotationView(final Context c) {
		this(c, true);
	}

	public AnnotationView(final Context c, boolean enableReshape) {
		super(c);
		this.enableReshape = enableReshape;
		instantiate(c);
	}

	protected void instantiate(Context c) {
		initValue(c);
		initAnnotationResizeViews();
		setOnTouchListener();

	}

	private void initValue(Context c) {

		this.context = c;
		final ViewConfiguration configuration = ViewConfiguration.get(this.context);
		touchSlop = configuration.getScaledTouchSlop();
		operationRadius = (int)context.getResources().getDimension(R.dimen.annotation_resize_area);
		miniHeight = operationRadius * 3;
		widthDefault = (int)getResources().getDimension(R.dimen.annotation_default_width);
		heightDefault = (int)getResources().getDimension(R.dimen.annotation_default_height);

		DisplayMetrics dm = new DisplayMetrics();
		((Activity)context).getWindowManager().getDefaultDisplay().getMetrics(dm);
		screenWidth = dm.widthPixels;
		screenHeight = dm.heightPixels;

		maxHeight = screenHeight - DensityUtil.getStatusBarHeight((Activity)context)
				- DensityUtil.getTitleBarHeight((Activity)context);
	}

	private void initAnnotationResizeViews() {

		LayoutParams paramsTopLeft = new LayoutParams(operationRadius, operationRadius);
		paramsTopLeft.addRule(ALIGN_PARENT_LEFT);
		paramsTopLeft.addRule(ALIGN_PARENT_TOP);
		closeView = new ImageView(context);
		closeView.setImageDrawable(getResources().getDrawable(R.drawable.close));
		addView(closeView, paramsTopLeft);

		LayoutParams paramsTextView = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
		paramsTextView.addRule(CENTER_IN_PARENT);
		paramsTextView.leftMargin = operationRadius / 2;
		paramsTextView.rightMargin = operationRadius / 2;
		paramsTextView.topMargin = operationRadius / 2;
		paramsTextView.bottomMargin = operationRadius / 2;

		annotationBoundView = new AnnotationBoundView(context);
		addView(annotationBoundView, paramsTextView);

		if (enableReshape) {
			LayoutParams paramsBottomRight = new LayoutParams(operationRadius, operationRadius);
			paramsBottomRight.addRule(ALIGN_PARENT_RIGHT);
			paramsBottomRight.addRule(ALIGN_PARENT_BOTTOM);
			reshapeView = new AnnotationOperationView(context, operationRadius / 2);
			addView(reshapeView, paramsBottomRight);
		}

	}

	protected abstract void initContentView();

	private void setOnTouchListener() {
		this.setOnTouchListener(this);
		closeView.setOnClickListener(close);
		if (enableReshape) {
			reshapeView.setOnTouchListener(this);
		}

	}

	protected View.OnClickListener close = new View.OnClickListener() {

		@Override
		public void onClick(View v) {
			doRemoveView();
		}
	};

	protected void doRemoveView() {
		ViewGroup annotationView = (ViewGroup)closeView.getParent();
		ViewGroup parent = (ViewGroup)annotationView.getParent();
		parent.removeView(annotationView);
		if (delegate != null) {
			delegate.postDelete(annotationView);
		}
	}

	@Override
	public boolean onTouch(View v, MotionEvent event) {

		if (v == reshapeView) {
			return handleReShapeView(v, event);
		} else {
			return handleMoveView(v, event);
		}

	}

	private boolean handleReShapeView(View v, MotionEvent event) {
		switch (event.getAction()) {
		case MotionEvent.ACTION_DOWN:
			mode = State.RESHAPE;
			initAnnotationParameters(v, event);
			break;
		case MotionEvent.ACTION_MOVE:
			updateAnnotationShape(v, event);
			break;
		case MotionEvent.ACTION_UP:
			mode = State.NONE;
			break;
		default:
			break;
		}
		return true;
	}

	private void updateAnnotationShape(View view, MotionEvent event) {
		ViewGroup annotationView = (ViewGroup)view.getParent();
		ViewGroup topView = (ViewGroup)annotationView.getParent();
		topView.updateViewLayout(annotationView, getAnnotationViewParamToBeUpdated(view, event));

	}

	private LayoutParams getAnnotationViewParamToBeUpdated(View v, MotionEvent event) {

		int width = 0;
		int height = 0;
		int left = 0;
		int top = 0;

		float currentX = event.getRawX();
		float currentY = event.getRawY();
		float dx = currentX - xStart;
		float dy = currentY - yStart;

		{
			width = Math.max(Math.abs(annotationInitialWidth + (int)(dx + 0.5f)), miniHeight);
			height = Math.max(Math.abs(annotationInitialHeight + (int)(dy + 0.5f)), miniHeight);
			left = currentAnnotationView.getLeft();
			top = currentAnnotationView.getTop();
		}
		LayoutParams params = new LayoutParams(width, height);
		params.leftMargin = left;
		params.topMargin = top;
		return params;
	}

	private boolean handleMoveView(View v, MotionEvent event) {
		switch (event.getAction() & MotionEvent.ACTION_MASK) {
		case MotionEvent.ACTION_DOWN:
			initAnnotationParameters(v, event);
			mode = State.DRAG;
			break;
		case MotionEvent.ACTION_POINTER_DOWN:
			disLastTouch = MathUtil.getZoomDistance(event);
			if (disLastTouch > 10f) {
				mode = State.ZOOM;
			}
			break;
		case MotionEvent.ACTION_UP:
			mode = State.NONE;
			break;
		case MotionEvent.ACTION_POINTER_UP:
			scaleLastTouch = scaleCurrent;
			mode = State.NONE;
			break;
		case MotionEvent.ACTION_CANCEL:
			mode = State.NONE;
			break;
		case MotionEvent.ACTION_MOVE:
			if (mode == State.DRAG) {
				updateAnnotationPosition(v, event);
			} else if (mode == State.ZOOM) {
				float newZoomDistance = MathUtil.getZoomDistance(event);

				if (newZoomDistance > touchSlop) {
					float scale = newZoomDistance / disLastTouch;
					updateAnnotationSize(v, event, scale);
				}
			}
			break;
		}
		return true;

	}

	private void updateAnnotationSize(View view, MotionEvent event, float scale) {

		/**
		 * if the annotation size reaches the limitation of screen, just return directly.
		 */
		if (annotationInitialWidth * scale > screenWidth || annotationInitialHeight * scale > maxHeight) {
			return;
		}

		scaleCurrent = MathUtil.clamp(scaleMini, scaleLastTouch * scale, scaleMax);

		/**
		 * update the param of the current annotation view
		 */
		{

			int newWidth = (int)(annotationInitialWidth * scale);
			int newHeight = (int)(annotationInitialHeight * scale);
			LayoutParams params = new LayoutParams(newWidth, newHeight);

			params.leftMargin = (int)(xPivot - newWidth / 2);
			params.topMargin = (int)(YPivot - newHeight / 2);

			/**
			 * this is to make sure that the annotation would not be hidden or shaped
			 */
			if (params.leftMargin < 0) {
				params.leftMargin = 0;
			}

			if (params.topMargin < 0) {
				params.topMargin = 0;
			}

			if (params.leftMargin + newWidth > screenWidth) {
				params.leftMargin = screenWidth - newWidth;
			}

			if (params.topMargin + newHeight > maxHeight) {
				params.topMargin = maxHeight - newHeight;
			}

			ViewGroup parent = (ViewGroup)view.getParent();
			parent.updateViewLayout(view, params);
		}

		/**
		 * update the content param in the current annotation view which is the text or image view.
		 */
		{
			LayoutParams paramsTextView = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
			paramsTextView.addRule(CENTER_IN_PARENT);
			paramsTextView.leftMargin = operationRadius / 2;
			paramsTextView.rightMargin = operationRadius / 2;
			paramsTextView.topMargin = operationRadius / 2;
			paramsTextView.bottomMargin = operationRadius / 2;
			updateContentView(scaleCurrent, paramsTextView);
		}
	}

	protected abstract void updateContentView(float scaleNew2, LayoutParams paramsTextView);

	private void initAnnotationParameters(View v, MotionEvent event) {
		if (v == this) {
			currentAnnotationView = (ViewGroup)v;
		} else {
			currentAnnotationView = (ViewGroup)v.getParent();
		}
		annotationInitialLeft = currentAnnotationView.getLeft();
		annotationInitialTop = currentAnnotationView.getTop();
		annotationInitialWidth = currentAnnotationView.getWidth();
		annotationInitialHeight = currentAnnotationView.getHeight();
		xPivot = currentAnnotationView.getLeft() + currentAnnotationView.getWidth() / 2;
		YPivot = currentAnnotationView.getTop() + currentAnnotationView.getHeight() / 2;
		xStart = (int)event.getRawX();
		yStart = (int)event.getRawY();
		if (delegate != null) {
			delegate.postInitParam(currentAnnotationView);
		}

	}

	private void updateAnnotationPosition(View view, MotionEvent event) {
		LayoutParams params = getAnnotationViewNewParam(view, event);
		ViewGroup parent = (ViewGroup)view.getParent();
		parent.updateViewLayout(view, params);
	}

	private LayoutParams getAnnotationViewNewParam(View view, MotionEvent event) {
		float currentX = event.getRawX();
		float currentY = event.getRawY();
		float dx = currentX - xStart;
		float dy = currentY - yStart;
		xLast = (int)(annotationInitialLeft + dx);
		yLast = (int)(annotationInitialTop + dy);
		int right = xLast + annotationInitialWidth;
		int bottom = yLast + annotationInitialHeight;
		if (xLast < 0) {
			xLast = 0;
		}
		if (yLast < 0) {
			yLast = 0;
		}

		if (right > screenWidth) {
			xLast = screenWidth - annotationInitialWidth;
		}

		if (bottom > maxHeight) {
			bottom = maxHeight;
			yLast = bottom - annotationInitialHeight;
		}

		if (delegate.getImageDocumentTopAndBottom() != null
				&& yLast < delegate.getImageDocumentTopAndBottom()[0]
						- DensityUtil.getStatusBarHeight((Activity)context)) {
			yLast = delegate.getImageDocumentTopAndBottom()[0] - DensityUtil.getStatusBarHeight((Activity)context);
		}

		if (delegate.getImageDocumentTopAndBottom() != null
				&& bottom > delegate.getImageDocumentTopAndBottom()[1]
						- DensityUtil.getStatusBarHeight((Activity)context)) {
			bottom = delegate.getImageDocumentTopAndBottom()[1] - DensityUtil.getStatusBarHeight((Activity)context);
			yLast = bottom - view.getHeight();
		}

		LayoutParams params = new LayoutParams(annotationInitialWidth, annotationInitialHeight);
		params.leftMargin = xLast;
		params.topMargin = yLast;
		return params;
	}

	public void setDelegate(AnnotationDelegate delegate) {
		this.delegate = delegate;
	}

	protected void setOperationInvisible() {
		closeView.setVisibility(View.GONE);
		annotationBoundView.setVisibility(View.GONE);
		if (enableReshape) {
			reshapeView.setVisibility(View.GONE);
		}
	}

	public void setOperationVisible() {
		closeView.setVisibility(View.VISIBLE);
		annotationBoundView.setVisibility(View.VISIBLE);
		if (enableReshape) {
			reshapeView.setVisibility(View.VISIBLE);
		}
	}

}